/*
    在五种IO模型中，效率最高的就是多路转接了
    多路转接一般用于网络编程中，可以用于同时处理多个IO通道，并且多路转接是不用使用多线程/多进程的，单进程即可实现
    这里，我们这是select来实现多路转接:
        #include <sys/select.h>
        int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    IO的本质就是等 + 拷贝，而多路转接的效率之所以高，就是因为select只负责等，而且可以同时等待多个fd，同时等待多个fd，就会导致拷贝的间隔时间越短
    而提高IO的本质就是缩短IO过程中，等的比重

    select 参数介绍：
        nfds：等待的所有文件描述符中，最大的文件描述符 + 1
        readfds：用来设置需要select关心的指定文件描述符的读时间是否就绪，即如果位图中被设置为1，则表示该位置的文件描述符select需要等待,关心该fd的读事件
        writefds，和exceptfds一样，分别是让select关心，读和异常事件是否就绪

        readfds, wiritefds, exceptfds都是输入输出型参数，调用时：用户->内核，我需要你关心那些fd的那些事件；返回时：内核->用户，你要我关心的fd的结果
        这也就是说，这三张位图结果会频繁的变化，并且我们让select关心时，不是只关心一次，而是每一次都要关心
        但是select返回的时候会修改原先位图，就会导致我们想让它关心的fd被取消掉了，所以我们需要借助一个辅助数组，将所有的fd保存起来
        每次调用select之前，都重新设置select需要关心的fd

        timeout:超时时间，timeval是一个结构体，有两个成员，__time_t tv_sec 表示秒 ， __suseconds_t tv_usec ,表示毫秒
                        如果 timeout 为nullptr，则表示select阻塞等待
                        如果 timeout 为 0，0 则表示select非阻塞等待
                        如果timeout不为0， 则表示在该时间内阻塞，超时之后，则非阻塞返回
                timeout也是一个输入输出型参数，输入时就是上面三种情况，而输出时，表示剩余时间，如果不为0，则表示在超时时间内事件就绪了
                                                                                        如果为0，则表示时间超时了，也没有事件就绪
*/
#include <iostream>
#include <unistd.h>
#include <sys/select.h>

void Recver(int fd) 
{
    char buffer[1024] = {0};
    int n = read(fd, buffer, sizeof(buffer) - 1);
    if(n > 0) {
        buffer[n - 1] = 0; // 将输入的回车给过滤掉
        std::cout << buffer << std::endl;
    }
}


int main()
{
    char buffer[1024] = {0};
    while(true) {
        // 不要直接读，会因为读事件默认没就绪，此时直接读的话，就会阻塞
        fd_set rfds;
        FD_ZERO(&rfds);    // 清空rfds
        FD_SET(0, &rfds);  // 将你想要关心的文件描述符，设置进该fd集合中，不要直接使用位操作

        struct timeval timeout;
        timeout.tv_sec = 2;
        timeout.tv_usec = 0;
        int n = select(0 + 1, &rfds, nullptr, nullptr, &timeout);
        if(n > 0) {
            // 有事件就绪了，此时返回值表示有多少个fd就绪了
            Recver(0);
        }
        else if(n == 0) {
            // 非阻塞返回
            std::cout << "没有事件就绪" << std::endl;
        }
        else {
            // select出错了
            std::cerr << "select error" << std::endl;
            break;
        }
    }
    return 0;
}